#include "Board.h"
#include "Gem.h"
#include "..\Engine\Core\Core.h"
#include "..\Engine\Logic\Object2DManager.h"
#include "..\Engine\GUI\GUIManager.h"
#include "..\Engine\GUI\GUIText.h"
#include "..\Engine\Sound\SoundManager.h"
#include "..\Engine\Particles\ParticleManager.h"

CBoard::CBoard(int32 rows, int32 columns)	
	: m_Rows(rows)
	, m_Columns(columns)
	, m_Score(0)
	, m_ScoreMultiplier(MIN_SCORE)
{
	m_BoardData.resize(rows);
	std::vector<TRow>::iterator l_it = m_BoardData.begin();
	for(; l_it != m_BoardData.end();++l_it)
	{
		l_it->resize(columns);
	}
}

CBoard::~CBoard()
{
	std::vector<TRow>::iterator l_it = m_BoardData.begin();
	for(; l_it != m_BoardData.end();++l_it)
	{
		l_it->clear();
	}
	m_BoardData.clear();
	m_FreeGems.clear();
}

void CBoard::Init(int32 x, int32 y, int32 Width, int32 Height)
{
	m_x = x;
	m_y = y;
	m_Width = Width;
	m_Height = Height;
	//Calculate containers positions and sizes
	float l_w = (float)Width / m_Columns; 
	float l_h = (float)Height/ m_Rows;
	m_ContainersWidth = int32(l_w);
	m_ContainersHeight = int32(l_h);
	for(int32 l_row = 0; l_row < m_Rows; ++l_row)
	{
		for(int32 l_col=0; l_col < m_Columns; ++l_col)
		{
			m_BoardData[l_row][l_col].w = l_w;
			m_BoardData[l_row][l_col].h = l_h;
			m_BoardData[l_row][l_col].x = x + l_col * l_w;
			m_BoardData[l_row][l_col].y = y + l_row * l_h;			
			m_BoardData[l_row][l_col].MarkedDown = false;
			m_BoardData[l_row][l_col].MarkedLeft = false;
			m_BoardData[l_row][l_col].MarkedRight = false;
			m_BoardData[l_row][l_col].MarkedUp = false;
			//Create random Gem...			
			CGem* l_gem = new CGem((CGem::EGemType)(rand() % 5));
			l_gem->SetBounds(m_BoardData[l_row][l_col].x,
							m_BoardData[l_row][l_col].y,
							m_BoardData[l_row][l_col].w,
							m_BoardData[l_row][l_col].h);
			//...add to object manager...
			CORE.GetObject2DManager()->AddResource(("gem_"+std::to_string((_LONGLONG) l_row)+"_"+std::to_string((_LONGLONG)l_col)).c_str(), l_gem);
			//...and assign to containers
			m_BoardData[l_row][l_col].Gem = l_gem;
		}
	}	
	Reset(); //prepare board
	m_eBoardState = BS_PAUSED;
}

void CBoard::SelectGem(const int32 x,const int32 y)
{
	if ((m_eBoardState == BS_WAIT_SEL1) || (m_eBoardState == BS_WAIT_SEL2)) //states that accept inputs
	{
		if ((x > m_x) && (y > m_y) && (x < m_x + m_Width) && (y < m_y + m_Height)) //into the board
		{
			int32 l_col = (x - m_x) / m_ContainersWidth;
			int32 l_row = (y - m_y) / m_ContainersHeight;
			if (m_eBoardState == BS_WAIT_SEL1)
			{
				m_SelectedRow1 = l_row;
				m_SelectedCol1 = l_col;
				m_BoardData[l_row][l_col].Gem->SetSelected(true);
				SetState(BS_WAIT_SEL2);
			}
			else if (m_eBoardState == BS_WAIT_SEL2)
			{
				if ((((abs(l_row - m_SelectedRow1) == 1) && (abs(l_col - m_SelectedCol1) == 0)) ||
					 ((abs(l_row - m_SelectedRow1) == 0) && (abs(l_col - m_SelectedCol1) <= 1))) &&
					 ((l_row != m_SelectedRow1) || (l_col != m_SelectedCol1)))
				{
					//Is next to selected 1
					m_SelectedRow2 = l_row;
					m_SelectedCol2 = l_col;					
					m_BoardData[m_SelectedRow1][m_SelectedCol1].Gem->SetSelected(false);
					SetState(BS_PLAY_ANIMATION_MOVE);
				}
				else
				{
					//new selected 1					
					m_BoardData[m_SelectedRow1][m_SelectedCol1].Gem->SetSelected(false);
					m_SelectedRow1 = l_row;
					m_SelectedCol1 = l_col;
					m_BoardData[l_row][l_col].Gem->SetSelected(true);
				}
			}
		}
	}
	
}

void CBoard::SwitchGems(int32 row1, int32 col1, int32 row2, int32 col2)
{
	//switch objects
	CGem* l_gem = m_BoardData[row1][col1].Gem;
	m_BoardData[row1][col1].Gem = m_BoardData[row2][col2].Gem;
	m_BoardData[row2][col2].Gem = l_gem;
	//setpositions
	m_BoardData[row1][col1].Gem->SetPosition(m_BoardData[row1][col1].x, m_BoardData[row1][col1].y);
	m_BoardData[row2][col2].Gem->SetPosition(m_BoardData[row2][col2].x, m_BoardData[row2][col2].y);
}

void CBoard::SetGem(int32 row, int32 col, CGem* Gem)
{
	m_BoardData[row][col].Gem = Gem;
	if (m_BoardData[row][col].Gem != NULL)
	{
		Gem->SetBounds( m_BoardData[row][col].x,
						m_BoardData[row][col].y,
						m_BoardData[row][col].w,
						m_BoardData[row][col].h);
		Gem->SetVisible(true);
	}
}

void CBoard::EvaluateGem(int32 row, int32 col)
{
	ContainerData l_data = m_BoardData[row][col];
	int32 l_Counter;
	//Evaluate Left
	if(!l_data.MarkedLeft)
	{
		// Count same gems to the left
		l_Counter = 0;
		for(int32 i=col-1; i >= 0; --i)
		{
			if ( m_BoardData[row][i].Gem->GetGemType() == l_data.Gem->GetGemType() )
			{
				l_Counter++;
			}
			else break;
		}
		// enough gems in line?
		if (l_Counter >= MIN_LINE_GEMS-1) 
		{
			//Mark Gems
			m_BoardData[row][col].MarkedLeft = true;
			for(int32 i=1; i <= l_Counter; ++i)
			{
				if (i == l_Counter) //last
				{
					m_BoardData[row][col-i].MarkedRight = true;
				}
				else
				{
					m_BoardData[row][col-i].MarkedRight = true;
					m_BoardData[row][col-i].MarkedLeft = true;
				}
			}
		}
	}

	//Evaluate Right
	if(!l_data.MarkedRight)
	{
		// Count same gems to the right
		l_Counter = 0;
		for(int32 i=col+1; i < m_Columns; ++i)
		{
			if ( m_BoardData[row][i].Gem->GetGemType() == l_data.Gem->GetGemType() )
			{
				l_Counter++;
			}
			else break;
		}
		// enough gems in line?
		if (l_Counter >= MIN_LINE_GEMS-1) 
		{
			//Mark Gems
			m_BoardData[row][col].MarkedRight = true;
			for(int32 i=1; i <= l_Counter; ++i)
			{
				if (i == l_Counter) //last
				{
					m_BoardData[row][col+i].MarkedLeft = true;
				}
				else
				{
					m_BoardData[row][col+i].MarkedRight = true;
					m_BoardData[row][col+i].MarkedLeft = true;
				}
			}
		}
	}

	//Evaluate Down
	if(!l_data.MarkedDown)
	{
		// Count same gems to the right
		l_Counter = 0;
		for(int32 i=row+1; i < m_Rows; ++i)
		{
			if ( m_BoardData[i][col].Gem->GetGemType() == l_data.Gem->GetGemType() )
			{
				l_Counter++;
			}
			else break;
		}
		// enough gems in line?
		if (l_Counter >= MIN_LINE_GEMS-1) 
		{
			//Mark Gems
			m_BoardData[row][col].MarkedDown = true;
			for(int32 i=1; i <= l_Counter; ++i)
			{
				if (i == l_Counter) //last
				{
					m_BoardData[row+i][col].MarkedUp = true;
				}
				else
				{
					m_BoardData[row+i][col].MarkedDown = true;
					m_BoardData[row+i][col].MarkedUp = true;
				}
			}
		}
	}

	//Evaluate Up
	if(!l_data.MarkedUp)
	{
		// Count same gems to the right
		l_Counter = 0;
		for(int32 i=row-1; i >= 0 ; --i)
		{
			if ( m_BoardData[i][col].Gem->GetGemType() == l_data.Gem->GetGemType() )
			{
				l_Counter++;
			}
			else break;
		}
		// enough gems in line?
		if (l_Counter >= MIN_LINE_GEMS-1) 
		{
			//Mark Gems
			m_BoardData[row][col].MarkedUp = true;
			for(int32 i=1; i <= l_Counter; ++i)
			{
				if (i == l_Counter) //last
				{
					m_BoardData[row-i][col].MarkedDown = true;
				}
				else
				{
					m_BoardData[row-i][col].MarkedDown = true;
					m_BoardData[row-i][col].MarkedUp = true;
				}
			}
		}
	}

}

bool CBoard::GemMarked(int32 row, int32 col)
{
	return	m_BoardData[row][col].MarkedDown ||
			m_BoardData[row][col].MarkedLeft ||	
			m_BoardData[row][col].MarkedRight ||
			m_BoardData[row][col].MarkedUp;
}

void CBoard::ClearMarked()
{
	//Mark to delete gems
	for(int32 i=0; i < m_Rows; ++i)
	{
		for(int32 j=0; j < m_Columns; ++j)
		{
			m_BoardData[i][j].MarkedDown = false;
			m_BoardData[i][j].MarkedLeft = false;
			m_BoardData[i][j].MarkedRight = false;
			m_BoardData[i][j].MarkedUp = false;
		}
	}
}

void CBoard::ClearSelected()
{	
	for(int32 i=0; i < m_Rows; ++i)
	{
		for(int32 j=0; j < m_Columns; ++j)
		{
			if (m_BoardData[i][j].Gem != NULL)
				m_BoardData[i][j].Gem->SetSelected(false);
		}
	}
}

bool CBoard::ExistMarked()
{	
	for(int32 i=0; i < m_Rows; ++i)
	{
		for(int32 j=0; j < m_Columns; ++j)
		{
			if ( m_BoardData[i][j].MarkedDown ||
				 m_BoardData[i][j].MarkedLeft ||
				 m_BoardData[i][j].MarkedRight||
				 m_BoardData[i][j].MarkedUp)
			{
				return true;
			}

		}
	}
	return false;
}

bool CBoard::ExistHoles(int32 col)
{
	bool l_bHoleFound = false;
	for(int32 i=m_Columns-1; i >= 0; --i)
	{
		if(m_BoardData[i][col].Gem == NULL)
		{
			l_bHoleFound = true;
		}
		else
		{
			if (l_bHoleFound) return true;
		}
	}
	return false;
}

bool CBoard::ColumnIsFull(int32 col)
{
	for(int32 i=0; i < m_Rows; ++i)
	{
		if(m_BoardData[i][col].Gem == NULL) return false;
	}
	return true;
}

void CBoard::Reset()
{
	for(int32 i=0; i < m_Rows; ++i)
	{
		for(int32 j=0; j < m_Columns; ++j)
		{
			m_BoardData[i][j].Gem->SetGemType((CGem::EGemType)(rand() % 5));
			SetGem(i,j, m_BoardData[i][j].Gem);
		}
	}
	EvaluateBoard();	
	if (ExistMarked())
	{
		Reset();
	}
}

void CBoard::AnimationMove(float dt)
{
	bool l_bAnimationEnded = false;
	ContainerData l_data1 = m_BoardData[m_SelectedRow1][m_SelectedCol1];
	ContainerData l_data2 = m_BoardData[m_SelectedRow2][m_SelectedCol2];
	if (l_data1.x == l_data2.x) //vertical
	{
		if (l_data1.y > l_data2.y)
		{
			l_data1.Gem->SetY(l_data1.Gem->GetY() - VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetY(l_data2.Gem->GetY() + VEL_ANIMATION_MOVE * dt);
			if (l_data1.Gem->GetY() <= l_data2.y) //end animation
				l_bAnimationEnded = true;
		}
		else
		{
			l_data1.Gem->SetY(l_data1.Gem->GetY() + VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetY(l_data2.Gem->GetY() - VEL_ANIMATION_MOVE * dt);
			if (l_data1.Gem->GetY() >= l_data2.y) //end animation
				l_bAnimationEnded = true;
		}		
	}
	else //horizontal
	{
		if (l_data1.x > l_data2.x)
		{
			l_data1.Gem->SetX(l_data1.Gem->GetX() - VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetX(l_data2.Gem->GetX() + VEL_ANIMATION_MOVE * dt);
			if(l_data1.Gem->GetX() <= l_data2.x) //end animation
				l_bAnimationEnded = true;
		}
		else
		{
			l_data1.Gem->SetX(l_data1.Gem->GetX() + VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetX(l_data2.Gem->GetX() - VEL_ANIMATION_MOVE * dt);
			if (l_data1.Gem->GetX() >= l_data2.x) //end animation
				l_bAnimationEnded = true;
		}
	}	
	if (l_bAnimationEnded)
	{
		SwitchGems(m_SelectedRow1, m_SelectedCol1, m_SelectedRow2, m_SelectedCol2);
		SetState(BS_EVALUATE_MOVE);
	}
}

void CBoard::AnimationRollback(float dt)
{
	bool l_bAnimationEnded = false;
	ContainerData l_data1 = m_BoardData[m_SelectedRow1][m_SelectedCol1];
	ContainerData l_data2 = m_BoardData[m_SelectedRow2][m_SelectedCol2];
	if (l_data1.x == l_data2.x) //vertical
	{
		if (l_data1.y > l_data2.y)
		{
			l_data1.Gem->SetY(l_data1.Gem->GetY() - VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetY(l_data2.Gem->GetY() + VEL_ANIMATION_MOVE * dt);
			if (l_data1.Gem->GetY() <= l_data2.y) //end animation
				l_bAnimationEnded = true;
		}
		else
		{
			l_data1.Gem->SetY(l_data1.Gem->GetY() + VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetY(l_data2.Gem->GetY() - VEL_ANIMATION_MOVE * dt);
			if (l_data1.Gem->GetY() >= l_data2.y) //end animation
				l_bAnimationEnded = true;
		}		
	}
	else //horizontal
	{
		if (l_data1.x > l_data2.x)
		{
			l_data1.Gem->SetX(l_data1.Gem->GetX() - VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetX(l_data2.Gem->GetX() + VEL_ANIMATION_MOVE * dt);
			if(l_data1.Gem->GetX() <= l_data2.x) //end animation
				l_bAnimationEnded = true;
		}
		else
		{
			l_data1.Gem->SetX(l_data1.Gem->GetX() + VEL_ANIMATION_MOVE * dt);
			l_data2.Gem->SetX(l_data2.Gem->GetX() - VEL_ANIMATION_MOVE * dt);
			if (l_data1.Gem->GetX() >= l_data2.x) //end animation
				l_bAnimationEnded = true;
		}
	}	
	if (l_bAnimationEnded)
	{
		SwitchGems(m_SelectedRow1, m_SelectedCol1, m_SelectedRow2, m_SelectedCol2);
		SetState(BS_WAIT_SEL1);
	}
}

void CBoard::AnimationExplosion(float dt)
{
	bool l_bAnimationEndeded = false;
	bool l_bWaitingExplosion = false;
	for(int32 i=0; i < m_Rows; ++i)
	{
		for(int32 j=0; j < m_Columns; ++j)
		{
			if (GemMarked(i,j))
			{								
				if (m_BoardData[i][j].Gem->GetWidth() >= m_BoardData[i][j].w + ADDED_SIZE)
				{
					l_bWaitingExplosion = true;
				}
				else
				{
					float l_w = m_BoardData[i][j].Gem->GetWidth() + VEL_EXPLOSION * dt;
					float l_h = m_BoardData[i][j].Gem->GetHeight() + VEL_EXPLOSION * dt;
					m_BoardData[i][j].Gem->SetSize(l_w, l_h);					
				}					
			}
		}
	}
	if (l_bWaitingExplosion)
	{
		if (m_TimeCounter > TIME_EXPLOSION)
		{
			l_bAnimationEndeded = true;
		}
		else
		{
			m_TimeCounter += dt;
		}
	}
	if (l_bAnimationEndeded)
	{
		for(int32 i=0; i < m_Rows; ++i)
		{
			for(int32 j=0; j < m_Columns; ++j)
			{
				if (GemMarked(i,j))
				{
					CGem* l_gem = m_BoardData[i][j].Gem;
					//reset size
					l_gem->SetSize(m_BoardData[i][j].w, m_BoardData[i][j].h);
					//not visible
					l_gem->SetVisible(false);
					//Eliminate from board
					m_BoardData[i][j].Gem = NULL;
					m_FreeGems.push_back(l_gem);
					//Add particles for gem
					uint8 l_r,l_g,l_b;
					switch(l_gem->GetGemType())
					{
						case CGem::GT_BLUE:
							l_r = 0;
							l_g = 255;
							l_b = 255;
							break;
						case CGem::GT_GREEN:
							l_r = 0;
							l_g = 255;
							l_b = 0;
							break;
						case CGem::GT_PURPLE:
							l_r = 230;
							l_g = 230;
							l_b = 250;
							break;
						case CGem::GT_RED:
							l_r = 255;
							l_g = 69;
							l_b = 0;
							break;
						case CGem::GT_YELLOW:
							l_r = 255;
							l_g = 255;
							l_b = 0;
							break;
					}
					float l_xcenter = m_BoardData[i][j].x + m_BoardData[i][j].w / 2;
					float l_ycenter = m_BoardData[i][j].y + m_BoardData[i][j].h / 2;
					CORE.GetParticleManager()->AddExplosion(l_xcenter, l_ycenter, l_r, l_g, l_b);
					//Add score
					AddScore();
					ShowScore( l_xcenter, l_ycenter, m_ScoreMultiplier);					
				}
			}
		}				
		SetState(BS_PLAY_ANIMATION_DOWNGEMS);
	}
}

void CBoard::AnimationDownGems(float dt)
{	
	bool l_bAnimationEnded = true;
	for(int32 i=0; i < m_Columns; ++i) //for every column
	{
		if (ExistHoles(i))
		{
			l_bAnimationEnded = false;
			AnimationDownColumn(i, dt);
		}
	}
	if (l_bAnimationEnded)
	{
		CORE.GetSoundManager()->PlaySound("hit.wav");
		SetState(BS_PLAY_ANIMATION_NEWGEMS);
	}
}

void CBoard::AnimationDownColumn(int32 col, float dt)
{
	for(int32 i=m_Rows-2; i >= 0 ;--i)
	{
		if (m_BoardData[i+1][col].Gem == NULL)
		{
			if (m_BoardData[i][col].Gem != NULL)
			{
				m_BoardData[i][col].Gem->SetY(m_BoardData[i][col].Gem->GetY() + VEL_ANIMATION_DOWNGEMS * dt);
				if (m_BoardData[i][col].Gem->GetY() >= m_BoardData[i+1][col].y)
				{
					SetGem(i+1, col, m_BoardData[i][col].Gem);
					SetGem(i, col, NULL);
				}
			}
		}
	}
}

void CBoard::AnimationNewGems(float dt)
{
	bool l_bAnimationEnded = true;
	for(int32 i=0; i < m_Columns; ++i) //for every column
	{
		if(!ColumnIsFull(i))
		{
			l_bAnimationEnded = false;
			if(m_BoardData[0][i].Gem == NULL)
			{
				//get unused gem
				CGem* l_gem = m_FreeGems.back();
				m_FreeGems.pop_back();
				//random type
				l_gem->SetGemType((CGem::EGemType)(rand() % 5));
				//set the gem to board
				SetGem(0, i, l_gem);				
			}
			if (ExistHoles(i))
			{
				AnimationDownColumn(i, dt);
			}
		}
	}
	if (l_bAnimationEnded)
	{
		CORE.GetSoundManager()->PlaySound("hit.wav");
		SetState(BS_EVALUATE_BOARD);
	}
}

void CBoard::EvaluateBoard()
{
	ClearMarked();
	for(int32 i=0; i < m_Rows; ++i)
	{
		for(int32 j=0; j < m_Columns; ++j)
		{
			//for every gem
			EvaluateGem(i,j);
		}
	}
}

void CBoard::ResetFreeGems()
{
	if (m_FreeGems.size() > 0)
	{
		std::vector<CGem*>::iterator l_it = m_FreeGems.begin();
		for(; l_it != m_FreeGems.end(); ++l_it)
		{			
			bool l_bGemAssigned = false;
			for(int32 i=0; i < m_Rows; ++i)
			{
				for(int32 j=0; j < m_Columns; ++j)
				{
					if(m_BoardData[i][j].Gem == NULL)
					{
						SetGem(i, j, *l_it);
						l_bGemAssigned = true;
						break;
					}					
				}
				if (l_bGemAssigned) break;
			}
		}
		m_FreeGems.clear();
	}
}

void CBoard::AddScore()
{
	m_ScoreMultiplier += MIN_SCORE;
	m_Score += m_ScoreMultiplier;
}

void CBoard::ShowScore(float x, float y, int32 Score)
{
	std::string l_name = "points"+std::to_string(_Longlong(x))+std::to_string(_Longlong(y))+std::to_string(_Longlong(Score));
	CGUIText* l_text = (CGUIText*) CORE.GetGUIManager()->AddTextBox(l_name, std::to_string(_Longlong(Score)), x, y);	
	l_text->SetVY(VEL_POINTS);
	l_text->SetLiveTime(TIME_POINTS);
	l_text->SetBold(true);
}

void CBoard::Stop()
{
	m_eBoardState = BS_PAUSED;				
	ClearSelected();
	ResetFreeGems();
	Reset();
}

void CBoard::Start()
{	
	m_Score = 0;
	m_ScoreMultiplier = MIN_SCORE;
	SetState(BS_WAIT_SEL1);
}

void CBoard::Update(float dt)
{
	switch(m_eBoardState)
	{
		case BS_PLAY_ANIMATION_MOVE:
			CORE.GetSoundManager()->PlaySound("switch.wav");
			m_TimeCounter = 0.f;
			SetState(BS_ANIMATION_MOVE);
			break;
		case BS_ANIMATION_MOVE:
			AnimationMove(dt);
			break;
		case BS_EVALUATE_MOVE:			
			EvaluateBoard();
			if (ExistMarked())
			{
				SetState(BS_PLAY_ANIMATION_EXPLOSION);
			}
			else
			{
				SetState(BS_PLAY_ANIMATION_ROLLBACK_MOVE);
			}
			break;
		case BS_PLAY_ANIMATION_ROLLBACK_MOVE:
			CORE.GetSoundManager()->PlaySound("switch.wav");
			m_TimeCounter = 0.f;
			SetState(BS_ANIMATION_ROLLBACK_MOVE);
			break;
		case BS_ANIMATION_ROLLBACK_MOVE:
			AnimationRollback(dt);
			break;
		case BS_PLAY_ANIMATION_EXPLOSION:
			CORE.GetSoundManager()->PlaySound("explosion.wav");
			m_TimeCounter = 0.f;
			SetState(BS_ANIMATION_EXPLOSION);
			break;
		case BS_ANIMATION_EXPLOSION:
			AnimationExplosion(dt);
			break;
		case BS_PLAY_ANIMATION_DOWNGEMS:
			m_TimeCounter = 0.f;
			SetState(BS_ANIMATION_DOWNGEMS);
			break;
		case BS_ANIMATION_DOWNGEMS:
			AnimationDownGems(dt);
			break;
		case BS_PLAY_ANIMATION_NEWGEMS:
			m_TimeCounter = 0.f;
			SetState(BS_ANIMATION_NEWGEMS);
			break;
		case BS_ANIMATION_NEWGEMS:
			AnimationNewGems(dt);
			break;
		case BS_EVALUATE_BOARD:
			EvaluateBoard();
			if (ExistMarked())
			{
				SetState(BS_PLAY_ANIMATION_EXPLOSION);
			}
			else
			{
				m_ScoreMultiplier = MIN_SCORE;
				SetState(BS_WAIT_SEL1);
			}
			break;
	}
}